#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>

#include "datatypes.h"
#include "model.h"
#include "utils.h"

Concept* get_create_atomic_concept(char* name, TBox* tbox) {
	Concept* c;

	// check if the atomic concept already exists
	if ((c = get_atomic_concept((unsigned char*) name, tbox)) != NULL)
		return c;

	// if it does not already exist, create it
	c = (Concept*) malloc(sizeof(Concept));
	assert(c != NULL);

	c->description.atomic = (AtomicConcept*) malloc(sizeof(AtomicConcept));
	assert(c->description.atomic != NULL);

	c->description.atomic->name = (char*) malloc((strlen(name) + 1) * sizeof(char));
	assert(c->description.atomic->name != NULL);

	c->type = ATOMIC_CONCEPT;
	strcpy(c->description.atomic->name, name);
	c->id = tbox->last_concept_id++;
	c->told_subsumers = NULL;
	c->told_subsumer_count = 0;
	c->subsumers = (Pvoid_t) NULL;
	c->filler_of_exists_restrictions = NULL;
	c->filler_of_count = 0;
	c->conjunct_of = NULL;
	c->conjunct_of_count = 0;
	c->description.atomic->direct_subsumers = (Pvoid_t) NULL;
	c->description.atomic->equivalent_concepts = NULL;
	c->description.atomic->equivalent_concept_count = 0;

	put_atomic_concept((unsigned char*) c->description.atomic->name, c, tbox);
	tbox->atomic_concept_count++;
	return c;
}


Role* get_create_atomic_role(char* name, TBox* tbox) {
	Role* r;

	// check if the atomic role already exists
	if ((r = get_atomic_role((unsigned char*) name, tbox)) != NULL)
		return r;

	// if it does not already exist, create it
	r = (Role*) malloc(sizeof(Role));
	assert(r != NULL);
	r->type = ATOMIC_ROLE;
	r->description.atomic = (AtomicRole*) malloc(sizeof(AtomicRole));
	assert(r->description.atomic != NULL);
	r->description.atomic->name = (char*) malloc((strlen(name) + 1) * sizeof(char));
	assert(r->description.atomic->name != NULL);
	strcpy(r->description.atomic->name, name);
	r->id = tbox->last_role_id++;
	put_atomic_role((unsigned char*) name, r, tbox);
	tbox->atomic_role_count++;
	return r;
}

// TODO: decide on the hash key
// get or create the existential restriction with role r and filler f
Concept* get_create_exists_restriction(Role* r, Concept* f, TBox* tbox) {
	Concept* c;

	tbox->exists_restriction_count++;
	// first check if we already created an existential
	// restriction with the same role and filler
	if ((c = get_exists_restriction(r->id,f->id, tbox)) != NULL)
		return c;

	// if it does not already exist, create it
	c = (Concept*) malloc(sizeof(Concept));
	assert(c != NULL);

	c->type = EXISTENTIAL_RESTRICTION;
	c->description.exists = (Exists*) malloc(sizeof(Exists));
	assert(c->description.exists != NULL);
	c->description.exists->role = r;
	c->description.exists->filler = f;
	c->id = tbox->last_concept_id++;
	c->told_subsumers = NULL;
	c->told_subsumer_count = 0;
	c->subsumers = (Pvoid_t) NULL;
	c->filler_of_exists_restrictions = NULL;
	c->filler_of_count = 0;
	c->conjunct_of = NULL;
	c->conjunct_of_count = 0;

	put_exists_restriction(r->id,f->id,c, tbox);
	tbox->unique_exists_restriction_count++;

	return c;
}

// TODO: first look up in the hash, if not order the conjuncts, put into hash
// get or create the existential restriction with the given conjuncts
Concept* get_create_conjunction(int size, Concept** conjuncts, TBox* tbox) {
	int i;
	Concept *c;

	tbox->conjunction_count++;
	// first check if we already created a conjunction
	// with the same conjuncts
	if ((c = get_conjunction(size, conjuncts, tbox)) != NULL)
		return c;

	c = (Concept*) malloc(sizeof(Concept));
	assert(c != NULL);

	c->type = CONJUNCTION;
	c->description.conj = (Conjunction*) malloc(sizeof(Conjunction));
	assert(c->description.conj != NULL);
	c->description.conj->size = size;
	c->description.conj->conjuncts = (Concept**) malloc(size * sizeof(Concept*));
	assert(c->description.conj->conjuncts != NULL);
	for (i=0; i < size; ++i)
		c->description.conj->conjuncts[i] = conjuncts[i];
	c->id = tbox->last_concept_id++;
	c->told_subsumers = NULL;
	c->told_subsumer_count = 0;
	c->subsumers = (Pvoid_t) NULL;
	c->filler_of_exists_restrictions = NULL;
	c->filler_of_count = 0;
	c->conjunct_of = NULL;
	c->conjunct_of_count = 0;

	put_conjunction(size, c, tbox);
	tbox->unique_conjunction_count++;

	return c;
}

// create the subclass axiom with the given concept descriptions
SubClassAxiom* create_subclass_axiom(Concept* lhs, Concept* rhs) {
	SubClassAxiom* ax = (SubClassAxiom*) malloc(sizeof(SubClassAxiom));
	assert(ax != NULL);
	ax->lhs = lhs;
	ax->rhs = rhs;
	return ax;
}

// TODO: get_create ...
// create the equivalent classes axiom with the given concept descriptions
EqClassAxiom* create_eqclass_axiom(Concept* lhs, Concept* rhs) {
	if (lhs->type != ATOMIC_CONCEPT) {
		fprintf(stderr,"the lhs of an equivalent classes axiom must be an atomic concept, aborting\n");
		exit(EXIT_FAILURE);
	}
	EqClassAxiom* ax = (EqClassAxiom*) malloc(sizeof(EqClassAxiom));
	assert(ax != NULL);
	ax->lhs = lhs;
	ax->rhs = rhs;
	return ax;
}

// add a given subclass axiom to a given TBox
void add_subclass_axiom(SubClassAxiom* ax, TBox* tbox) {
	SubClassAxiom** tmp;
	tmp = realloc(tbox->subclass_axioms, (tbox->subclass_axiom_count + 1) * sizeof(SubClassAxiom*));
	assert(tmp != NULL);
	tbox->subclass_axioms = tmp;
	tbox->subclass_axioms[tbox->subclass_axiom_count] = ax;
	tbox->subclass_axiom_count++;
}

// add a given equivalent classes axiom to a given TBox
void add_eqclass_axiom(EqClassAxiom* ax, TBox* tbox) {
	EqClassAxiom** tmp;
	tmp = realloc(tbox->eqclass_axioms, (tbox->eqclass_axiom_count + 1) * sizeof(EqClassAxiom*));
	assert(tmp != NULL);
	tbox->eqclass_axioms = tmp;
	tbox->eqclass_axioms[tbox->eqclass_axiom_count] = ax;
	++tbox->eqclass_axiom_count;
}

// add a given subrole axiom to a given TBox
void add_subrole_axiom(SubRoleAxiom* ax, TBox* tbox) {
	SubRoleAxiom** tmp;
	tmp = realloc(tbox->subrole_axioms, (tbox->subrole_axiom_count + 1) * sizeof(SubRoleAxiom*));
	assert(tmp != NULL);
	tbox->subrole_axioms = tmp;
	tbox->subrole_axioms[tbox->subrole_axiom_count] = ax;
	++tbox->subrole_axiom_count;
}
